#include "data/cfg_items.hpp"
#include <cassert>
#include <sstream>

IValueAdapter::~IValueAdapter() = default;

RenderringFunc CfgItemBase::pFnRender = nullptr;

CfgItemBase::CfgItemBase(CfgType type, std::string title, std::string help, bool enabled)
:type_(type) ,title_(std::move(title)), title_hidden_("##"+title_), help_(std::move(help)), enabled_(enabled)
{
    
}

CfgItemBase::~CfgItemBase() {}

void CfgItemBase::Render()
{
    if (CfgItemBase::pFnRender) {
        CfgItemBase::pFnRender(this);
    }
}

bool CfgItemBase::Enabled() const {
    return enabled_;
}

void CfgItemBase::Load() {

}

bool CfgItemBase::Save(std::ostream &err_out) {
    return true;
}

bool CfgItemBase::Save() {
    std::ostringstream oss;
    return Save(oss);
}


CfgGroup::CfgGroup(std::string title, const std::initializer_list<CfgItemPtr>& children, std::string help, bool enabled)
: CfgItemBase(CfgType::Group, std::move(title), std::move(help), enabled), show_title_(!title_.empty())
{
    children_.assign(begin(children), end(children));
}

CfgGroup::CfgGroup(std::string title, std::string help, bool enabled)
: CfgItemBase(CfgType::Group, std::move(title), std::move(help), enabled), show_title_(!title_.empty())
{

}

CfgGroup::~CfgGroup()
{

}

void CfgGroup::AddItem(CfgItemPtr child)
{
    children_.emplace_back(std::move(child));
}

void CfgGroup::AddItems(const std::initializer_list<CfgItemPtr>& children)
{
    for (auto x: children)
    {
        AddItem(std::move(x));
    }
}


void CfgGroup::Load()
{
    for (auto &x: children_) {
        x->Load();
    }
}

bool CfgGroup::Save(std::ostream &err_out)
{
    for (auto &x: children_) {
        if (!x->Save(err_out)) {
            return false;
        }
    }
    return true;
}

CfgEditable::CfgEditable(IValueAdapter *adapter, CfgType type, std::string title, std::string help, bool enabled)
: CfgItemBase(type, std::move(title), std::move(help), enabled), adapter_(adapter)
{
    assert(adapter_ != nullptr && "null adapter?");
}

CfgEditable::~CfgEditable()
{
    delete adapter_;
}

void CfgEditable::Load()
{
    vcache_ = adapter_->get();
}

bool CfgEditable::Save(std::ostream &err_out)
{
    return adapter_->set(vcache_, err_out);
}

CfgAction::CfgAction(Callback cb, bool sameline, std::string title, std::string help, bool enabled)
: CfgItemBase(CfgType::Action, std::move(title), std::move(help), enabled), sameline_(sameline), callback_(cb)
{

}

CfgAction::~CfgAction()
{

}

CfgText::CfgText(IValueAdapter *adapter, std::string title, std::string help, bool enabled)
: CfgEditable(adapter, CfgType::Text, std::move(title), std::move(help), enabled)
{

}

CfgText::~CfgText()
{

}


CfgIntegral::CfgIntegral(bool is_signed, IValueAdapter *adapter, std::string title, std::string help, bool enabled)
: CfgEditable(adapter, CfgType::Integral, std::move(title), std::move(help), enabled), is_signed_(is_signed)
{

}

CfgIntegral::~CfgIntegral()
{

}

void CfgIntegral::Load()
{
    CfgEditable::Load();
    char *end;
    if (is_signed_) {
        i64_ = std::strtoll(vcache_.data(), &end, 10);
    } else {
        u64_ = std::strtoull(vcache_.data(), &end, 10);
    }
}

bool CfgIntegral::Save(std::ostream &err_out)
{
    vcache_ = std::to_string(is_signed_ ? i64_ : u64_);
    return CfgEditable::Save(err_out);
}

CfgReal::CfgReal(IValueAdapter *adapter, std::string title, std::string help, bool enabled)
: CfgEditable(adapter, CfgType::FloatingPoint, std::move(title), std::move(help), enabled)
{

}

CfgReal::~CfgReal()
{

}

void CfgReal::Load()
{
    CfgEditable::Load();
    char *end;
    value_ = std::strtod(vcache_.data(), &end);
}

bool CfgReal::Save(std::ostream &err_out)
{
    vcache_ = std::to_string(value_);
    return CfgEditable::Save(err_out);
}

CfgSwitch::CfgSwitch(std::string on_value, std::string off_value, IValueAdapter *adapter, std::string title, std::string help, bool enabled)
: CfgEditable(adapter, CfgType::FloatingPoint, std::move(title), std::move(help), enabled), on_value_(std::move(on_value)), off_value_(std::move(off_value))
{

}

CfgSwitch::~CfgSwitch()
{

}

void CfgSwitch::Load()
{
    CfgEditable::Load();
    value_ = vcache_ == on_value_;
}

bool CfgSwitch::Save(std::ostream &err_out)
{
    vcache_ = value_ ? on_value_ : off_value_;
    return CfgEditable::Save(err_out);
}

CfgChoice::CfgChoice(const std::vector<std::string> &options, IValueAdapter *adapter, std::string title, std::string help, bool enabled)
: CfgEditable(adapter, CfgType::FloatingPoint, std::move(title), std::move(help), enabled)
{
    options_.assign(begin(options), end(options));
}

CfgChoice::~CfgChoice()
{

}

void CfgChoice::Load()
{
    CfgEditable::Load();
    selected_ = -1;
    for (int i=0; i<options_.size(); ++i) {
        if (options_[i] == vcache_) {
            selected_ = i;
            break;
        }
    }
}

bool CfgChoice::Save(std::ostream &err_out)
{
    if (selected_ >= 0 && selected_ < options_.size()) {
        vcache_ = options_[selected_];
    } else {
        vcache_.clear();
    }
    return CfgEditable::Save(err_out);
}

#include "data/cfg_adapters.hpp"


void Foo() {
    int a ;
    NoCheckedValue<int> nv(&a);
    RangedValue<int> rv(&a, 10, 20);


    delete NEW_NC(a);
    delete NEW_RC(a, 100, 200);
}
